namespace SS.Common.Lock
{
    #region Used namespaces

    using System.Threading;

    #endregion

    /// <summary>
    /// Atomic boolean.
    /// </summary>
    public struct AtomicBoolean
    {
        private int flag;

        /// <summary>
        /// Unset value
        /// </summary>
        private const int UnSet = 0;

        /// <summary>
        /// Set value
        /// </summary>
        private const int Set = 1;

        /// <summary>
        /// Compare and exchange value
        /// </summary>
        /// <param name="expected">
        /// The expected.
        /// </param>
        /// <param name="newVal">
        /// The new val.
        /// </param>
        /// <returns>
        /// true if they equals
        /// </returns>
        public bool CompareAndExchange(bool expected, bool newVal)
        {
            var newTemp = newVal ? Set : UnSet;
            var expectedTemp = expected ? Set : UnSet;

            return Interlocked.CompareExchange(ref this.flag, newTemp, expectedTemp) == expectedTemp;
        }

        /// <summary>
        /// Convert from a bool value
        /// </summary>
        /// <param name="value">
        /// The bool value.
        /// </param>
        /// <returns>
        /// The created object
        /// </returns>
        public static AtomicBoolean FromValue(bool value)
        {
            var temp = new AtomicBoolean { Value = value };

            return temp;
        }

        /// <summary>
        /// Exchange current value
        /// </summary>
        /// <param name="newVal">
        /// The new value.
        /// </param>
        /// <returns>
        /// Return the original value
        /// </returns>
        public bool Exchange(bool newVal)
        {
            var newTemp = newVal ? Set : UnSet;
            return Interlocked.Exchange(ref this.flag, newTemp) == Set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether Value.
        /// </summary>
        public bool Value
        {
            get
            {
                return this.flag == Set;
            }

            set
            {
                Thread.VolatileWrite(ref this.flag, value ? Set : UnSet);
            }
        }

        /// <summary>
        /// Compare two <see cref="AtomicBoolean"/> objects
        /// </summary>
        /// <param name="rhs">Object to compare</param>
        /// <returns>
        /// true if equals
        /// </returns>
        public bool Equals(AtomicBoolean rhs)
        {
            return this.flag == rhs.flag;
        }

        /// <summary>
        /// Compare two <see cref="AtomicBoolean"/> objects
        /// </summary>
        /// <param name="rhs">Object to compare</param>
        /// <returns>
        /// true if equals
        /// </returns>
        public override bool Equals(object rhs)
        {
            return rhs is AtomicBoolean ? this.Equals((AtomicBoolean)rhs) : false;
        }

        /// <summary>
        /// get hash code.
        /// </summary>
        /// <returns>
        /// The calculated hash code.
        /// </returns>
        public override int GetHashCode()
        {
            return this.flag.GetHashCode();
        }

        /// <summary>
        /// Convert from <see cref="AtomicBoolean"/> to a bool
        /// </summary>
        /// <param name="rhs">The object</param>
        /// <returns>
        /// bool value
        /// </returns>
        public static explicit operator bool(AtomicBoolean rhs)
        {
            return rhs.Value;
        }

        /// <summary>
        /// Convert from a bool value to <see cref="AtomicBoolean"/> object
        /// </summary>
        /// <param name="rhs">The bool value</param>
        /// <returns>
        /// Converted object
        /// </returns>
        public static implicit operator AtomicBoolean(bool rhs)
        {
            return FromValue(rhs);
        }
    }
}
